package common

import (
    "bytes"
    "fmt"
    "strconv"
    "strings"
    "sync"
    "time"
    "regexp"
)



var datapointPool sync.Pool

func ParseDataPoint(data []byte) (*DataPoint, error) {
    fields := strings.Split(string(data), " ")
    if len(fields) != 3 || len(fields[0]) == 0 {
        return nil, fmt.Errorf("data format wrong")
    }
    val, err := strconv.ParseFloat(string(fields[1]), 64)
    if err != nil {
        return nil, err
    }
    ts, err := strconv.ParseUint(string(fields[2]), 10, 0)
    if err != nil {
        return nil, err
    }
    v := datapointPool.Get()
    if v==nil{
        return &DataPoint{fields[0], val, uint32(ts)}, nil
    }
    dp := v.(*DataPoint)
    dp.Name=fields[0]
    dp.Val=val
    dp.Time = uint32(ts)
    return dp, nil
}
func ReleaseDatapoint(dp *DataPoint){
    datapointPool.Put(dp)
}
func Day(ts uint32) string {
    d := time.Unix(int64(ts), 0)
    return fmt.Sprintf("%04d%02d%02d", d.Year(), d.Month(), d.Day())
}
func HMS(ts uint32,) string {
    d := time.Unix(int64(ts), 0)
    return fmt.Sprintf("%02d%02d%02d", d.Hour(),d.Minute(),d.Second())
}

func HMSInt(ts uint32,) int {
    d := time.Unix(int64(ts), 0)
    return d.Hour()*10000+d.Minute()*100+d.Second()
}

func DayInt(ts uint32) int {
    d := time.Unix(int64(ts), 0)
    return d.Year()*10000+ int(d.Month())*100+ d.Day()
}
func ExtractKey(name string) (ret int32) {
    ret = 0
    if v := strings.Split(name, "."); len(v) > 1 {
        if s := strings.Split(name, "."); len(s) == 4 {
            if i, err := strconv.Atoi(s[3]); err == nil {
                ret = int32(i)
            }
        }
    }
    return
}

var GoliaPatterns = []*regexp.Regexp{
    regexp.MustCompile(`^golia\.(?P<ip>\d+_\d+_\d+_\d+)\.(?P<head>(host|tcp4|load|Misc|cpu\.cpu_times|mem\.percent|net\.iorate))\.(?P<sub>.*)$`),
    regexp.MustCompile(`^golia\.(?P<ip>\d+_\d+_\d+_\d+)\.(?P<head>(disk\.iorate|disk\.percent|cpu\.core))\.(?P<path>[^\.]+)\.(?P<sub>.*)$`),
}

func Extract(metricName string)map[string]string{
    ret := make(map[string]string)
    for _, r := range GoliaPatterns {
        match := r.FindStringSubmatch(metricName)
        if match != nil {
            for i, name := range r.SubexpNames() {
                if i == 0 || name == "" {
                    continue
                }
                v :=match[i]
                if name=="ip"{
                    v=strings.Replace(v,"_",".",-1)
                }
                ret[name] = v
            }
            break
        }
    }
    return ret
}

type DataPoint struct {
    Name string
    Val  float64
    Time uint32
}

type DataTuple struct {
    Val  float64
    Time uint32
}

type OffsetValue struct {
    offset int
    value  float64
}

func (o OffsetValue) ToDataTuple(split TimeSplit) DataTuple {
    return DataTuple{o.value, uint32(split.base) + uint32(o.offset)}
}

func ConvertDataTuple(t []byte, v []byte) (*DataTuple, error) {
    ts, err := strconv.ParseUint(string(t), 10, 0)
    if err != nil {
        return nil, err
    }
    val, err := strconv.ParseFloat(string(v), 64)
    if err != nil {
        return nil, err
    }
    return &DataTuple{val, uint32(ts)}, nil
}

func ConvertDataPoint(key []byte, value []byte) (*DataPoint, error) {
    fields := bytes.Fields(key)
    if len(fields) != 2 {
        return nil, fmt.Errorf("key field wrong")
    }
    var err error
    val, err := strconv.ParseFloat(string(value), 64)
    if err != nil {
        return nil, fmt.Errorf("value field wrong")
    }
    ts, err := strconv.ParseUint(string(fields[1]), 10, 0)
    if err != nil {
        return nil, fmt.Errorf("timestamp field wrong")
    }

    v := datapointPool.Get()
    if v==nil{
        return &DataPoint{string(fields[0]), val, uint32(ts)}, nil
    }
    dp := v.(*DataPoint)
    dp.Name=string(fields[0])
    dp.Val=val
    dp.Time = uint32(ts)
    return dp, nil
}

func (d DataPoint) GetTuple() DataTuple {
    return DataTuple{Val: d.Val, Time: d.Time}
}

func (d DataPoint) GetKey() []byte {
    return []byte(fmt.Sprintf("%s %d", d.Name, d.Time))
}

func (d DataPoint) GetValue() []byte {
    return []byte(fmt.Sprintf("%f", d.Val))
}

func (d DataPoint) String() string {
    return d.Formated()
}
func (d DataPoint) Formated() string {
    return fmt.Sprintf("%s %f %d", d.Name, d.Val, d.Time)
}

func (d DataPoint)ValuesByDay() (ip, m string, day int, v float64) {
    ret := Extract(d.Name)
    day = DayInt(d.Time)
    if len(ret) == 0 {
        return
    }
    ip = ret["ip"]
    h := ret["head"]
    s := ret["sub"]
    p := ret["path"]
    v = d.Val
    if p == "" {
        m = strings.Join([]string{h, s}, ".")
        return
    }
    if strings.Contains(s, "disk") {
        if pp, err := Decode_base64(p); err == nil {
            p = pp
        }
    }
    m = strings.Join([]string{h, s, p}, ".")
    return
}


func (d DataPoint) Encode() ([]byte, error) {
    return []byte(fmt.Sprintf("%s %f %d", d.Name, d.Val, d.Time)), nil
}

func (d DataPoint) Length() int {
    return len(fmt.Sprintf("%s %f %d", d.Name, d.Val, d.Time))
}

